/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include "a_sync.h"
#include "a_config.h"
/* includes (standard library, system) ---------------------------------------*/
#include <stdint.h>
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <x_common.h>
#include <x_module.h>
#include <x_bug.h>
#include <hal_it.h>

/* defines -------------------------------------------------------------------*/

#define NVIC_PRIO_BITS CONFIG_NVIC_PRIO_BITS

/* typedefs ------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables (global) --------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/

static struct {
    /* interrupt set-enable registers */
    volatile uint32_t iser[16]; /* offset 0x000 */
    uint32_t pad0[16];
    /* interrupt clear-enable registers */
    volatile uint32_t icer[16]; /* offset 0x080 */
    uint32_t pad1[16];
    /* interrupt set-pending registers */
    volatile uint32_t ispr[16]; /* offset 0x100 */
    uint32_t pad2[16];
    /* interrupt clear-pending registers */
    volatile uint32_t icpr[16]; /* offset 0x180 */
    uint32_t pad3[24];
    /* interrupt active bit register */
    volatile uint32_t iabr[16]; /* offset 0x200 */
    uint32_t pad4[48];
    /* interrupt priority register */
    volatile uint8_t ipr[496]; /* offset 0x300 */
    uint32_t pad5[580];
    /* software trigger interrupt register */
    volatile uint32_t stir; /* offset 0xe00 */
} *const nvic = (void *)0xe000e100;

/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

static int nvic_setprio(unsigned int irq, unsigned int prio)
{
    nvic->ipr[irq] = (uint8_t)((prio << (8u - NVIC_PRIO_BITS)) & (uint32_t)0xffu);

    return 0;
}

static int nvic_enable(unsigned int irq)
{
    // each register has 32 (2^5) bits
    nvic->iser[irq >> 5] = (1 << (irq & 0x1f));

    return 0;
}

static int nvic_disable(unsigned int irq)
{
    nvic->icer[irq >> 5] = (1 << (irq & 0x1f));

    dsb();
    isb();

    return 0;
}

/**
 * nvic_init - nvic initialization routine
 *
 * return: 0 on success, negative value on error
 */
static int nvic_init(void)
{
    static const hal_it_methods_t nvic_methods = {
        .enable = nvic_enable,
        .disable = nvic_disable,
        .setprio = nvic_setprio,
    };

    BUG_ON(hal_it_register(&nvic_methods) != 0, "Fail to register NVIC!");

    return 0;
}

MODULE_INIT(service, nvic_init);
